#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/types.h>
#include <linux/ide.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

/**********************   defines   **********************/


/**********************   defines   **********************/
#define BEEP_NAME "beep"
#define BEEP_CNT  1
#define BEEP_DTS_PATH  "/huafvbeep"

#define BEEP_OFF    1		/* beep off */
#define BEEP_ON  	0		/* beep on */
/**********************   struct defines   **********************/
struct beep_dev {
    dev_t dev_id;           /* dev id   */
    int major;              /* major id */
    int minor;              /* minor id */
    struct cdev cdev;       /* cdev     */
    struct class *class;
    struct device *device;
    struct device_node * node;
    int beep_gpio;
};


struct beep_dev core_beep_dev;

/**********************   Function prototype   **********************/
static int  __init beep_init(void);
static void __exit beep_exit(void);

static int beep_open(struct inode *inode, struct file *file);
static int beep_release(struct inode *inode, struct file *file);
static ssize_t beep_read(struct file *file, char *buf, size_t size, loff_t *offset);
static ssize_t beep_write(struct file *file, const char *buf, size_t size, loff_t *offset);

void user_beep_switch(u8 status);

struct file_operations beep_fops = {
    .open = beep_open,
    .release = beep_release,
    .read = beep_read,
    .write = beep_write
};

/**********************   user defines   **********************/
module_init(beep_init);
module_exit(beep_exit)

MODULE_LICENSE("GPL");
MODULE_AUTHOR("huafv");

/**********************   Function implementation   **********************/
static int __init beep_init(void) {
    int ret = 0;
    printk(KERN_INFO "beep beep init\n");
    /* register device */
    core_beep_dev.major = 0;
    core_beep_dev.minor = 0;
    if(core_beep_dev.major) {
        core_beep_dev.dev_id = MKDEV(core_beep_dev.major, 0);
        register_chrdev_region(core_beep_dev.dev_id,  BEEP_CNT, BEEP_NAME);
    }else {
        alloc_chrdev_region(&core_beep_dev.dev_id, 0, BEEP_CNT, BEEP_NAME);
        core_beep_dev.major = MAJOR(core_beep_dev.dev_id);
        core_beep_dev.minor = MINOR(core_beep_dev.dev_id);
    }

    core_beep_dev.cdev.owner = THIS_MODULE;
    cdev_init(&core_beep_dev.cdev, &beep_fops);
    cdev_add(&core_beep_dev.cdev, core_beep_dev.dev_id, BEEP_CNT);

    core_beep_dev.class = class_create(THIS_MODULE, BEEP_NAME);
    if(IS_ERR(core_beep_dev.class)) {
        printk(KERN_ERR "can't create class\n");
    }

    core_beep_dev.device = device_create(core_beep_dev.class, NULL, core_beep_dev.dev_id, NULL, BEEP_NAME);
    if(IS_ERR(core_beep_dev.device)) {
        printk(KERN_ERR "can't create device\n");
    }

    /* find device node */
    core_beep_dev.node = of_find_node_by_path(BEEP_DTS_PATH);

    if(IS_ERR(core_beep_dev.node)) {
        printk(KERN_ERR "can't find node\n");
    }

    /* get gpio */
    core_beep_dev.beep_gpio  = of_get_named_gpio(core_beep_dev.node, "beep-gpios", 0);

    /* request gpio */
    ret = gpio_request(core_beep_dev.beep_gpio, "beep-gpio");
    if(ret < 0) {
        printk(KERN_ERR "gpio_request failed\n");
        gpio_free(core_beep_dev.beep_gpio);
        ret = gpio_request(core_beep_dev.beep_gpio, "beep-gpio");
    }

    /* gpio set dir */
    gpio_direction_output(core_beep_dev.beep_gpio, 0);

    /* gpio set value */
    gpio_set_value(core_beep_dev.beep_gpio, BEEP_ON);


    return 0;
}
static void __exit beep_exit(void) {
    printk(KERN_INFO "beep exit\n");
    gpio_set_value(core_beep_dev.beep_gpio, BEEP_OFF);
    gpio_free(core_beep_dev.beep_gpio);
    device_destroy(core_beep_dev.class, core_beep_dev.dev_id);
    class_destroy(core_beep_dev.class);
    cdev_del(&core_beep_dev.cdev);
    unregister_chrdev_region(core_beep_dev.dev_id, BEEP_CNT);
}


static int beep_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "beep open\n");
    return 0;
}
static int beep_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "beep release\n");
    return 0;
}
static ssize_t beep_read(struct file *file, char *buf, size_t size, loff_t *offset) {
    printk(KERN_INFO "beep read\n");
    return 0;
}
static ssize_t beep_write(struct file *file, const char *buf, size_t size, loff_t *offset) {
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;
    printk(KERN_INFO "beep write\n");

    retvalue = copy_from_user(databuf, buf, size);
    if(retvalue < 0) {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }

    ledstat = databuf[0];		/* 获取状态值 */

    if(ledstat == BEEP_ON) {
        user_beep_switch(BEEP_ON);		/* 打开LED灯 */
    } else if(ledstat == BEEP_OFF) {
        user_beep_switch(BEEP_OFF);	    /* 关闭LED灯 */
    }
    return 0;
}

void user_beep_switch(u8 status) {
    printk(KERN_INFO "beep_switch %d\n", status);
    if(status == BEEP_ON) {
        gpio_set_value(core_beep_dev.beep_gpio, 0);
    }else if(status == BEEP_OFF) {
        gpio_set_value(core_beep_dev.beep_gpio, 1);
    }
}
// /home/huafv/linux/nfs/rootfs/lib/modules/4.1.15-g3dc0a4b